﻿//----------------------------------------------------------------
// Desc: 
// Edit: ShowLong, http://blog.csdn.net/showlong
//
// Date Created: 2009-10-15
//
// Update Date :            
//
// Copyright (c) Fuel4D (http://www.fuel4d.com), All rights reserved.
//
//----------------------------------------------------------------
#pragma once


class FUI_System;


//----------------------------------------------------------------
// UI界面对象
//----------------------------------------------------------------
class FUI_SYS_WIDGET
{
    FUI_DECLARE_STRUCT(FUI_SYS_WIDGET)

    friend class FUI_System;
    friend class FUI_SYS_DRAG_FRINGE;
    friend class FUI_SYS_DRAG_CTRL;

protected:
    FUI_SYS_WIDGET(const WCHAR* filename, FUI_Layout* layout, FUI_Frame* pUI)
    {
        pLayout = layout;
        pFrame = pUI;
        hFileHash = FUI_GetHashW(filename);
        assert(pFrame);
    }
    FINLINE void Render(){ pFrame->Render(eURL_Picture); }
    FINLINE bool IsChild(FUI_Object* pChild){ return pFrame->IsChild(pChild); }
    FINLINE bool IsVisible(){ return pFrame->IsVisible(); }
    FINLINE bool PtInObject(funit x, funit y){ return pFrame->PtInObject(x, y); }
    FINLINE bool PtInObject(fuPoint pt){ return pFrame->PtInObject(pt.nX, pt.nY); }
    FINLINE bool IsDraging(){ return pFrame->IsDraging(); }
    FINLINE void GetRealRect(fuRect* rc){ return pFrame->GetRealRect(rc); }

    FIDHASH     hFileHash;
    FUI_Layout* pLayout;
    FUI_Frame*  pFrame;

public:
    ~FUI_SYS_WIDGET()
    {
        FUI_DelRelease(pFrame);
    }
};
typedef FUI_Vector< FUI_SYS_WIDGET* > fuv_fu_sys_pwidget;


//----------------------------------------------------------------
// 边缘拖动控制器
//----------------------------------------------------------------
class FUI_SYS_DRAG_FRINGE
{
    FUI_DECLARE_STRUCT(FUI_SYS_DRAG_FRINGE)

    friend class FUI_System;

protected:
    FUI_SYS_DRAG_FRINGE(){ clear(); }
    void clear(){ pUI = 0; bState[0] = bState[1] = bState[2] = bState[3] = false; }
    bool start(fuPoint& pt, FUI_Object* ui, bool* state);
    bool proc_mouse_move(fuPoint& pt, FUI_SYS_WIDGET* pwidget);
    bool proc_mouse_down(fuPoint& pt, FUI_SYS_WIDGET* pwidget);

    FUI_System* pUIMgr;
    fuPoint ptStart;
    FUI_Object* pUI;
    bool bState[4]; // 上下左右
};


//----------------------------------------------------------------
// 控件之间拖拽器
//----------------------------------------------------------------
class FUI_SYS_DRAG_CTRL
{
    FUI_DECLARE_STRUCT(FUI_SYS_DRAG_CTRL)

    friend class FUI_System;

protected:
    FUI_SYS_DRAG_CTRL(){ clear(); }
    void clear(){ pUI = 0; bMoveOut = false; }
    bool proc_mouse_move(fuPoint& pt, FUI_SYS_WIDGET* pwidget);
    bool proc_mouse_down(fuPoint& pt, FUI_SYS_WIDGET* pwidget);
    bool proc_mouse_up(fuPoint& pt, FUI_SYS_WIDGET* pwidget);

    FUI_System* pUIMgr;
    FUI_Object* pUI;
    bool bMoveOut;
};


//----------------------------------------------------------------
// 待处理事务对象
//----------------------------------------------------------------
enum EF_UI_SYS_EVENT
{
    eUSE_AddLayout,
    eUSE_DelLayout,
    eUSE_ToTop,
    eUSE_ToBottom,
};
struct FUI_SYS_EVENT
{
    FUI_DECLARE_STRUCT(FUI_SYS_EVENT);

    union DATA
    {
        void* addr;
        FIDHASH hash;
        FUI_SYS_WIDGET* widget;
        FUI_Layout* layout;
        FUI_Frame* frame;
        FUI_Object* ctrl;
    } data;
    EF_UI_SYS_EVENT type;

    FUI_SYS_EVENT(){}
    FUI_SYS_EVENT(EF_UI_SYS_EVENT t, void* p){ type = t, data.addr = p; }
    FUI_SYS_EVENT(EF_UI_SYS_EVENT t, FIDHASH h){ type = t, data.hash = h; }
};
typedef FUI_Vector< FUI_SYS_EVENT > fuv_fu_sys_event;


//----------------------------------------------------------------
// 事件输入
//----------------------------------------------------------------
enum EF_UI_INPUT_EVENT
{
    eUIE_MOUSEMOVE,             //
    eUIE_MOUSEWHEEL,            // nk 表示是否是向上滚，1向上，0向下
    eUIE_LBUTTONDOWN,           //
    eUIE_RBUTTONDOWN,           //
    eUIE_LBUTTONUP,             //
    eUIE_RBUTTONUP,             //
    eUIE_LBUTTONDBLCLK,         //
    eUIE_KEYDOWN,               // nk 表示虚拟键码
    eUIE_KEYUP,                 // nk 表示虚拟键码
    eUIE_CHAR,                  // nk 表示按键值
    eUIE_ACTIVATE,              // nk 表示活动状态，1活动，0非活动
    eUIE_SETCURSOR,             //
};


//----------------------------------------------------------------
// UI管理器
//----------------------------------------------------------------
class FAPI FUI_System : public FUI_AutoClassFactory< FUI_Object, EF_UI_WIDGET_TYPE >
{
	friend class FUI_Drawer_Register;
    friend class FUI_Object;
    friend class FUI_Frame;
    friend class FUI_Menu;
    friend class FUI_Drawer;
    friend class FUI_Layout;
    friend class FUI_Button;
    friend class FUI_Edit;
    friend class FUI_RichEdit;
    friend class FUI_ScrollBar;

    FUI_DECLARE_CLASS(FUI_System)

public:
    FUI_System();
    ~FUI_System();

    // 初始化UI管理器
    void Initialize(const ACHAR* filename, FUI_INIT* it);
    // 释放数据
    void Release();
    // 刷新UI系统
    void Update();
    // 绘制
    void Render();
    // 绘制 Tips
    void RenderTips();
    // 绘制铺助信息，以供调试
    void RendHelper(float x, float y);
    // 重置设备
    void Reset(funit wnd_w, funit wnd_h, bool force_update = false);
    // 消息事件处理
    bool InputEvent(EF_UI_INPUT_EVENT e, fuPoint& cur, unsigned int nk = 0);

    // 通过layout查询一个框架
    FUI_Frame* FindFrame(FUI_Layout* pLayout);
    // 通过FRAME的ID查询Layout
    FUI_Layout* FindLayout(FIDHASH frame_id);
    // 顶层UI是否是模式框架
    bool IsModelWnd();
    // 顶层UI是否是独占的
    bool IsExclusiveWnd();
    // 项层UI是否是强制置顶模式
    bool IsTopWnd();
    // 隐藏所有可见框架(对于UI编辑中设定的始终显示的UI不理踩)
    bool HideAllDialog(DWORD nParam = 0);
    // 将某个框架移到顶点，返回上一个顶部框架
    FUI_Frame* ToTop(FUI_Frame* pObject, bool bForce = false);
    // 将某个框架移动底层，返回上一个底部框架
    FUI_Frame* ToBottom(FUI_Frame* pObject);
    // 取得某个按钮状态(测试VK_LBUTTON和VK_RBUTTON请用函数GetMouseState)
    bool GetKeyState(DWORD key);
    bool GetMouseState(bool left_button);
    // 弹出错误提示
    WORD ErrorMessage(WORD error_no);
    void ErrorMessage(const ACHAR* format, ...);
    // 修正某个区域的正确窗区域，使它不会超过游戏框架
    void GetSafeRect(fuRect* in_out, funit border);
    // 设置UI显示状态
    void SetShowUI(bool show);
    // 设置焦点UI，并非框架(通常指某个框架中某个编辑框)
    void SetFocus(FUI_Object* pObj);
    // 设置当前打开的COMBO
    // .sel 打开列表后选中哪一条，填0为清空列表的选中项
    void SetOpenCombo(FUI_Combo* cb, const WCHAR* sel);
    // 查询是否含有可见框架，计算时排除指定组（该组具体由Layout来使用的）
    bool GetVisibleFrameState(DWORD exclude_group = 0);
    // 传送事件消息，不进入该界面的消息泵
    WORD SendMessage(FUI_Layout* layout, FIDHASH nWidgetID, EF_UI_EVENT_TYPE nMsgID, DWORD wParam = 0, DWORD lParam = 0);
    // 把某个已经创建过的UI弹出来显示
    bool PopupUI(FIDHASH h);
    // 清空渲染器中缓存的资源（通常可以在切地图的时候调用）
    void ClearDrawResource();
    // 重新初始化FUD数据（编辑器使用）
    void ReInitialize(FUIWidgetManager& wm);
    // 获取相对目录位置
    FUI_StringW& GetDir(FUI_StringW& ret, const WCHAR* dir);
    FUI_StringA& GetDir(FUI_StringA& ret, const ACHAR* dir);

    // UI是否可以显示
    FINLINE bool IsShowUI(){ return bNeedShowUI; }
    // 鼠标有没有在UI上
    FINLINE bool IsMouseInUI(){ return pMouseFrame != 0; }
    // 取得顶层框架(仅仅是取得顶层框架)
    FINLINE FUI_Object* GetFocusFrame(){ if (vpUI.size() > 0) return vpUI[0]->pFrame; return 0; }
    // 取得顶层框架(仅仅是取得顶层框架)
    FINLINE FUI_Layout* GetFocusLayout(){ if (vpUI.size() > 0) return vpUI[0]->pLayout; return 0;}
    // 取得焦点UI
    FINLINE FUI_Object* GetFocusObject(){ return pFocusObject; }
    // 取得当前悬停UI
    FINLINE FUI_Object* GetSuspendObject(){ return pSuspendObject; }
    // 取得当前鼠标所指的UI
    FINLINE FUI_Object* GetMouseFrame(){ return pMouseFrame; }
    // 当前按下的是哪一个按钮
    FINLINE FUI_Button* GetDownButton(){ return pDownButton; }
    // 取得菜单数据地址
    FINLINE FUI_Menu* GetMenu(){ return &uMenu; }
    // 取消鼠标指向的UI框架地扯
    FINLINE void CancelSuspend(){ if (pSuspendObject) { pSuspendObject->OnMoveOut(); pSuspendObject = 0; } }
    // 取得当前系统中框架数量
    FINLINE int GetFrameCount(){ return (int)vpUI.size(); }
    // 非常用数据获取，请不要随意修改
    FINLINE FUIWidgetManager& GetWidgetMgr(){ return uWidgetMgr; }
    FINLINE FUI_Drawer& GetDrawer(){ return uDrawMgr; }
    FINLINE void NeedRefResourceNT(){ nResNT = FUI_GetTime(); }

    // 动态注册一张贴图
    // .dst 需要保存到的目标贴图数据
    // .filename 存到在UI目录的ufile中的文件名
    bool RegisterPicture(fuPicture* dst, const WCHAR* filename, int w, int h);
    bool RegisterPicture(fuPicture* dst, const ACHAR* filename, int w, int h);
    // 已知图片HASH数据情况下初始化一张图片（强制）
    // .pic 提供图片的HASH，生成目录数据地址
    bool BindPicture(fuPicture* pic);
    // 非强制初始化
    bool TryBindPicture(fuPicture* pic);
    // 设置一条显示TIPS
    // .str TIPS的文字内容
    // .dx,dy TIPS的显示屏幕位置
    void SetDrawTips(const WCHAR* str, float dx, float dy);
    // 渲染图片
    // .reversal 翻转模式，0不翻转，1横向，2纵向，3横纵都翻
    FINLINE void DrawPicture(fuRect* dstRect, fuPicture* srcPic){ uDrawMgr.Draw(dstRect, srcPic); }
    FINLINE void DrawPicture(fuRect* dstRect, fuPicture* srcPic, BYTE reversal){ uDrawMgr.Draw(dstRect, srcPic, reversal); }
    // 消毁贴图资源（清理内存）
    FINLINE void DestroyPicture(fuPicture* srcPic){ uDrawMgr.Destroy(srcPic); }
    // 清理贴图数据（清理内存）
    FINLINE void ClearPicture(){ uDrawMgr.Release(); }

protected:
    // 添加一个框架
    WORD CreateLayout(const WCHAR* filename, FUI_Layout* layout);
    WORD CreateLayout(const ACHAR* filename, FUI_Layout* layout);
    // 从内存添加一个框架
    WORD CreateLayout(BYTE* mem, size_t size, const WCHAR* name, FUI_Layout* layout);
    WORD CreateLayout(BYTE* mem, size_t size, const ACHAR* name, FUI_Layout* layout);
    // 添加一个框架（编辑器使用）
    WORD CreateLayout(FUIWidgetDataFile& wdf, const WCHAR* name, FUI_Layout* layout);
    WORD CreateLayout(FUIWidgetDataFile& wdf, const ACHAR* name, FUI_Layout* layout);
    // 移除一个框架
    bool DestroyLayout(FUI_Layout* layout);
    // 创建FUI_SYS_WIDGET
    FUI_SYS_WIDGET* CreateWidget(const WCHAR* filename, FUI_Layout* layout);
    FUI_SYS_WIDGET* CreateWidget(const ACHAR* filename, FUI_Layout* layout);
    // 完成创建FUI_SYS_WIDGET
    WORD FinishWidget(FUI_SYS_WIDGET* stUI);
    // 将FUI_Layout之余指定的框架之上，使其永远显示
    //bool SetLayoutPos(FUI_Layout* pLay, FUI_Layout* pObject);
    // 刷新鼠标目标
    void RefMouseTarget();
    // 排序框架之间关系
    void Compositor();
    // 将第一个可见的排到最前端
    void RefreshFirstVisibleToTop();
    // 将鼠标所指框架，并尝试移到顶层（和RefMouseTarget功能有点相似）
    FUI_SYS_WIDGET* GetCursorObjectTryToTop(funit nX, funit nY);
    // 数据初始化以后的逻辑初始化处理
    void __InitEnd();
    // 此组函数是用于线程和调用安全用的，外部不需要调用
    void __AddFrame(FUI_SYS_WIDGET* sw);
    bool __DelFrame(FUI_Frame* frame);
    void __ToTop(FUI_Frame* pf);
    void __ToBottom(FUI_Frame* pf);

protected:
    FUIWidgetManager uWidgetMgr;    // 管理器地址
    FUI_Drawer uDrawMgr;            // 图片渲染管理器
    FUI_Menu uMenu;                 // UI菜单
    FUI_SYS_DRAG_CTRL uDGControl;   // 控件拖拽功能
    FUI_SYS_DRAG_FRINGE uDGFrange;  // UI框架或者UI控件尺寸拖动变化

    FUI_Object* pFocusObject;       // 焦点UI对象
    FUI_Object* pSuspendObject;     // 悬停UI对象
    FUI_Combo* pOpenCombo;          // 当前打开的COMBO
    FUI_Frame* pMouseFrame;         // 悬停UI框架
    FUI_Button* pDownButton;        // 上次按下的是哪一个按钮
    bool bNeedShowUI;               // 是否显示UI
    BYTE nFrameState;               // 帧处理状态（0空闲，1更新中，2渲染中）在处理状态中的时候，所有需要影响框架表顺序的事件放到下一帧处理
    bool bMouseState[2];            // 鼠标左右键的状态
    DWORD nResNT;                   // 资源树刷新时间
    FUI_StringW szWorkTips;         // 当前需要显示的TIPS文字
    fuRect tipRect;                 // TIPS的显示位置

    fuv_fu_sys_pwidget vpUI;        // UI框架表
    fuv_fu_sys_event vpEvent;       // 待处理事务

public:
    fuPoint ptCutsor;               // 当前鼠标坐标(框架坐标)
    FUI_INIT uIT;                   // 各接口参数（开放访问，请勿随意修改）
    int tipTxtFrame;                // tips的文字边框
    int tipWndFrame;                // tips显示的时候，距离窗体的边框
    DWORD tipColor;                 // tips显示的文本颜色
};